Recall that the .NET delegate type is essentially a type-safe, object-oriented, function pointer. When you declare a .NET delegate, the C# compiler responds by building a sealed class that derives from System.MulticastDelegate (which in turn derives from System.Delegate). These base classes provide every delegate with the ability to maintain a list of method addresses, all of which may be invoked at a later time. Consider the BinaryOp delegate first defined in Chapter 11:
// A C# delegate type. public delegate int BinaryOp(int x, int y);
Based on its definition, BinaryOp can point to any method taking two integers (by value) as arguments and returning an integer. Once compiled, the defining assembly now contains a full-blown class definition that is dynamically generated when you build your project, based on the delegate declaration. In the case of BinaryOp, this class looks more or less like the following (shown in pseudocode):
public sealed class BinaryOp : System.MulticastDelegate { public BinaryOp(object target, uint functionAddress); public void Invoke(int x, int y); public IAsyncResult BeginInvoke(int x, int y, AsyncCallback cb, object state); public int EndInvoke(IAsyncResult result); }
Recall that the generated Invoke() method is used to invoke the methods maintained by a delegate object in a synchronous manner. Therefore, the calling thread (such as the primary thread of the application) is forced to wait until the delegate invocation completes. Also recall that in C#, the Invoke() method does not need to be directly called in code, but can be triggered indirectly under the hood when applying “normal” method invocation syntax.
Consider the following Console Application program (SyncDelegateReview), which invokes the static Add() method in a synchronous (a.k.a. blocking) manner (be sure to import the System.Threading namespace, as you will be calling the Thread.Sleep() method).
namespace SyncDelegateReview { public delegate int BinaryOp(int x, int y); class Program { static void Main(string[] args) { Console.WriteLine("***** Synch Delegate Review *****"); // Print out the ID of the executing thread. Console.WriteLine("Main() invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId); // Invoke Add() in a synchronous manner. BinaryOp b = new BinaryOp(Add); // Could also write b.Invoke(10, 10); int answer = b(10, 10); // These lines will not execute until // the Add() method has completed. Console.WriteLine("Doing more work in Main()!"); Console.WriteLine("10 + 10 is {0}.", answer); Console.ReadLine(); } static int Add(int x, int y) { // Print out the ID of the executing thread. Console.WriteLine("Add() invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId); // Pause to simulate a lengthy operation. Thread.Sleep(5000); return x + y; } } }
Within the Add() method, you are invoking the static Thread.Sleep() method to suspend the calling thread for approximately five seconds to simulate a lengthy task. Given that you are invoking the Add() method in a synchronous manner, the Main() method will not print out the result of the operation until the Add() method has completed.
Next, note that the Main() method is obtaining access to the current thread (via Thread.CurrentThread) and printing out the ID of the thread via the ManagedThreadId property. This same logic is repeated in the static Add() method. As you might suspect, given that all the work in this application is performed exclusively by the primary thread, you find the same ID value displayed to the console:
***** Synch Delegate Review ***** Main() invoked on thread 1. Add() invoked on thread 1. Doing more work in Main()! 10 + 10 is 20. Press any key to continue . . .
When you run this program, you should notice that a five-second delay takes place before you see the final Console.WriteLine() logic in Main() execute. Although many (if not most) methods may be called synchronously without ill effect, .NET delegates can be instructed to call their methods asynchronously if necessary.
Source Code The SyncDelegateReview project is located under the Chapter 19 subdirectory.